﻿using Newtonsoft.Json;
using System;
using System.CodeDom;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Windows.Forms.VisualStyles;
using static System.Collections.Specialized.BitVector32;


namespace Tool.Helper
{
    public class IniHelper
    {
        public string _filePath;
        private int _maxLength = 4096;
        public IniEntity IniInfo { get; set; }
        public List<IniSectionKeyEntity> Keys { get; set; }
        /// <summary>
        /// 设置文件路径
        /// </summary>
        /// <param name="filePath"></param>
        /// <returns></returns>
        public bool SetFilePath(string filePath)
        {
            if (!Path.IsPathRooted(filePath))
            {
                string currentDirectory = Directory.GetCurrentDirectory();
                filePath = currentDirectory + "\\" + filePath;
            }
            if (!File.Exists(filePath))
            {
                FileStream fs = new FileStream(filePath, FileMode.OpenOrCreate);
                fs.Close();
                fs.Dispose();
                return false;
            }
            _filePath = filePath;
            GetAll();
            return true;
        }
        /// <summary>
        /// 修改INI文件内容
        /// </summary>
        /// <param name="lpApplicationName">节点名称(段落名称)section</param>
        /// <param name="lpKeyName">要设置的项名，Key</param>
        /// <param name="lpString">要写入的新字符串Value</param>
        /// <param name="lpFileName">INI文件晚挣路径</param>
        /// <returns>0表示失败，非零表示成功</returns>
        [DllImport("kernel32")]
        private static extern long WritePrivateProfileString(string lpApplicationName, string lpKeyName, string lpString, string lpFileName);

        /// <summary>
        /// 获取INI中指定字符串
        /// </summary>
        /// <param name="lpAppName">节点名称(段落名称)section</param>
        /// <param name="lpKeyName">项名，Key</param>
        /// <param name="lpDefault">未找到指定项时返回的默认值</param>
        /// <param name="lpReturnedString">指定一个字符串缓冲区，长度至少为nSize</param>
        /// <param name="nSize">指定装载到lpReturnedString缓冲区的最大字符数量</param>
        /// <param name="lpFileName">INI文件路径</param>
        /// <returns>复制到lpReturnedString中的字节数量</returns>
        [DllImport("kernel32")]
        private static extern int GetPrivateProfileString(string lpAppName, string lpKeyName, string lpDefault, StringBuilder lpReturnedString, int nSize, string lpFileName);

        /// <summary>
        /// 根据section，key取值,并设置默认值
        /// </summary>
        /// <param name="section">节点/段落名称</param>
        /// <param name="key">项/Key名称</param>
        /// <param name="def">默认值</param>
        /// <param name="filePath">文件路径</param>
        /// <returns>返回指定内容，若不存在则返回默认值def</returns>
        public string ReadIniKey(string section, string key, string def, string filePath)
        {
            StringBuilder temp = new StringBuilder(_maxLength);
            GetPrivateProfileString(section, key, def, temp, _maxLength, filePath);
            return ReadReplace(temp.ToString());
        }
        /// <summary>
        /// 根据section，key取值,并设置默认值
        /// </summary>
        /// <param name="section">节点/段落名称</param>
        /// <param name="key">项/Key名称</param>
        /// <param name="def">默认值</param>
        /// <returns>返回指定内容，若不存在则返回默认值def</returns>
        public string ReadIniKey(string section, string key, string def)
        {
            if (string.IsNullOrEmpty(_filePath))
            {
                return "";
            }
            StringBuilder temp = new StringBuilder(_maxLength);
            GetPrivateProfileString(section, key, def, temp, _maxLength, _filePath);
            return ReadReplace(temp.ToString());
        }

        /// <summary>
        /// 根据section，key取值,并设置默认值
        /// </summary>
        /// <param name="section">节点/段落名称</param>
        /// <param name="key">项/Key名称</param>
        /// <returns>返回指定内容，若不存在则返回默认值def</returns>
        public string ReadIniKey(string section, string key)
        {
            if (string.IsNullOrEmpty(_filePath))
            {
                return "";
            }
            StringBuilder temp = new StringBuilder(_maxLength);
            GetPrivateProfileString(section, key, null, temp, _maxLength, _filePath);
            return ReadReplace(temp.ToString());
        }

        /// <summary>
        /// 保存ini
        /// </summary>
        /// <param name="section">节点/段落名称</param>
        /// <param name="key">项/Key名称</param>
        /// <param name="value">值</param>
        /// <param name="filePath">ini文件路径</param>
        public bool WriteIniKey(string section, string key, string value, string filePath)
        {
            WritePrivateProfileString(section, key, WriteReplace(value), filePath);
            return true;
        }
        /// <summary>
        /// 保存ini
        /// </summary>
        /// <param name="section">节点/段落名称</param>
        /// <param name="key">项/Key名称</param>
        /// <param name="value">值</param>
        public bool WriteIniKey(string section, string key, string value)
        {
            if (string.IsNullOrEmpty(_filePath))
            {
                return false;
            }
            WritePrivateProfileString(section, key, WriteReplace(value), _filePath);
            return true;
        }

        /// <summary>
        /// 将数据JSON化后存入key
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="section">节点</param>
        /// <param name="key">Key</param>
        /// <param name="value">数据</param>
        /// <returns></returns>
        public bool WriteIniKey<T>(string section,string key,T value)
        {
            if (string.IsNullOrEmpty(_filePath))
            {
                return false;
            }
            var str = JsonConvert.SerializeObject(value);
            WritePrivateProfileString(section, key, WriteReplace(str), _filePath);
            return true;
        }

        /// <summary>
        /// 删除ini文件下所有段落
        /// </summary>
        public void ClearAllSection()
        {
            WritePrivateProfileString(null, null, null, _filePath);
        }
        /// <summary>
        /// 删除ini文件下personal段落下的所有键
        /// </summary>
        /// <param name="Section"></param>
        public void ClearSection(string Section)
        {
            WritePrivateProfileString(Section, null, null, _filePath);
        }

        /// <summary>
        /// 从读取到的INI文件信息中筛选与字段名相同的信息，并返回
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public T Get<T>() where T : new()
        {
            T result = new T();
            if (Keys != null && Keys.Count > 0)
            {
                var props = result.GetType().GetProperties();
                foreach (var prop in props)
                {
                    var dd = Keys.FirstOrDefault(p => p.Key== prop.Name);
                    if (dd != null)
                    {
                        var type = prop.PropertyType;
                        var data = JsonConvert.DeserializeObject(dd.Value,type);
                        prop.SetValue(result, data);
                    }
                }
            }
            return result;
        }
        /// <summary>
        /// JSON化时有[]，写入INI文件中会乱行
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        private string WriteReplace(string str)
        {
            str = str.Replace("[", "!-");
            str = str.Replace("]", "-!");
            return str;
        }
        /// <summary>
        /// JSON化时有[]，写入INI文件中会乱行
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        private string ReadReplace(string str)
        {
            str = str.Replace("!-", "[");
            str = str.Replace("-!", "]");
            return str;
        }
        
        /// <summary>
        /// 将对象写入INI，如存在和字段名一样的KEY写则入
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="value"></param>
        public void Write<T>(T value)
        {
            var props = value.GetType().GetProperties();
            foreach (var prop in props)
            {
                var data = Keys.FirstOrDefault(p => p.Key == prop.Name);
                if (data != null)
                {
                  var dd=  prop.GetValue(value);
                    var str = JsonConvert.SerializeObject(dd);
                   
                    WriteIniKey(data.Section, data.Key, WriteReplace(str));
                }
            }
        }

        /// <summary>
        /// 获取INI文件中的所有信息
        /// </summary>
        /// <returns></returns>
        public IniEntity GetAll()
        {
            IniInfo = new IniEntity();
            Keys = new List<IniSectionKeyEntity>();
            if (string.IsNullOrEmpty(_filePath))
            {
                return IniInfo;
            }
            string lineStr = "";
            string curSection = "";
            string patternSection = @"\[(.*?)\]"; ;
            string patternKey = @"^[^=]*=[^=]*$";
            IniSectionEntity stction = new IniSectionEntity();
            using (StreamReader sr = new StreamReader(_filePath))
            {
                while ((lineStr = sr.ReadLine()) != null)
                {
                    if (Regex.IsMatch(lineStr, patternSection))
                    {
                        Match match = Regex.Match(lineStr, patternSection);
                        if (match.Success)
                        {
                            curSection = match.Groups[1].Value;
                            var isAny = IniInfo.Sections.Any(p => p.SectionName == curSection);
                            if (!isAny)
                            {
                                IniInfo.Sections.Add(new IniSectionEntity { SectionName = curSection });
                            }
                        }
                    }
                    if (Regex.IsMatch(lineStr, patternKey))
                    {
                        
                           var strArr = lineStr.Split('=');
                        var key = strArr[0].Trim();
                        var value = ReadReplace(strArr[1].Trim()) ;
                        var isAny = IniInfo.Sections.Any(p => p.SectionName == curSection && p.Keys.Any(s => s.Key == key));
                        if (!isAny)
                        {
                            var isSectionAny = IniInfo.Sections.Any(p => p.SectionName == curSection);
                            var keyInfo = new IniSectionKeyEntity() { Key = key, Value = value, Section = curSection };
                            if (isSectionAny)
                            {
                                IniInfo.Sections.Find(p => p.SectionName == curSection).Keys.Add(keyInfo);
                            }
                            Keys.Add(keyInfo);
                        }
                    }
                }
            }
            return IniInfo;
        }
    }

    #region //Entity
    public class IniEntity
    {
        public List<IniSectionEntity> Sections { get; set; } = new List<IniSectionEntity>();
    }
    public class IniSectionEntity
    {
        public string SectionName { get; set; }
        public List<IniSectionKeyEntity> Keys { get; set; } = new List<IniSectionKeyEntity>();
    }
    public class IniSectionKeyEntity
    {

        public string Section { get; set; }
        public string Key { get; set; }
        public string Value { get; set; }
       

    }

    #endregion
}
